﻿using System;
using System.Text;
using PostSharp.Aspects;
using System.Reflection;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;

namespace Framework.Common.AOP.Performance
{
    /// <summary>
    /// 性能测试（适用于 win7 server2008 以上系统, windows2003以下自己网上找方案）
    /// </summary>
    [Serializable]
    public sealed class PerformanceAttribute : OnMethodBoundaryAspect
    {
        private StringBuilder performanceBuilder;
        private DateTime startTime;
        private ulong cycleCount;
        private int[] gcCounts;

        public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
        {
            performanceBuilder = new StringBuilder();
            cycleCount = 0;
        }

        public override void OnEntry(MethodExecutionArgs eventArgs)
        {
            string key = Formatter.Format(eventArgs);
            eventArgs.MethodExecutionTag = key;

            #region caculate time and cpu start
            // make the thread priority high 
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            Thread.CurrentThread.Priority = ThreadPriority.Highest;

            // 1.
            performanceBuilder.AppendLine();
            performanceBuilder.AppendLine(key + "  ========Start==========");

            // 2.
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            gcCounts = new int[GC.MaxGeneration + 1];
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                gcCounts[i] = GC.CollectionCount(i);
            }

            // 3.

            startTime = DateTime.Now;
            #endregion
        }

        // Executed at runtime, after the method.
        public override void OnSuccess(MethodExecutionArgs eventArgs)
        {
            string key = (string)eventArgs.MethodExecutionTag;

            ulong cpuCycles = GetCycleCount() - cycleCount;
            var ts = DateTime.Now - startTime;

            // 4.
            performanceBuilder.AppendLine("\tTime Elapsed:\t" + ts.TotalMilliseconds.ToString("N0") + "ms");
            performanceBuilder.AppendLine("\tCPU Cycles:\t" + cpuCycles.ToString("N0"));

            // 5.
            for (int i = 0; i <= GC.MaxGeneration; i++)
            {
                int count = GC.CollectionCount(i) - gcCounts[i];
                performanceBuilder.AppendLine("\tGen " + i + ": \t\t" + count);
            }

            //6
            performanceBuilder.AppendLine(key + "  ==========End===========");
            performanceBuilder.AppendLine();
            LogHelper.Debug(performanceBuilder);
        }


        #region windows api
        private static ulong GetCycleCount()
        {
            ulong cycleCount = 0;
            QueryThreadCycleTime(GetCurrentThread(), ref cycleCount);
            return cycleCount;
        }

        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool QueryThreadCycleTime(IntPtr threadHandle, ref ulong cycleTime);

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentThread();
        #endregion
    }
}
